using System.Security.Cryptography;
using System.Text;

namespace SumerCoreDevOps.Infrastructure.Security;

/// <summary>
/// 加密解密辅助类
/// 用于数据库中敏感数据的加密解密
/// </summary>
public static class EncryptionHelper
{
    private static byte[]? _cachedKey;
    private static readonly object _keyLock = new();
    private static string? _injectedKey; // 从 EncryptionKeyManager 注入的密钥

    /// <summary>
    /// 设置加密密钥（由 EncryptionKeyManager 调用）
    /// </summary>
    public static void SetEncryptionKey(string key)
    {
        lock (_keyLock)
        {
            _injectedKey = key;
            _cachedKey = null; // 清除缓存，强制重新生成
        }
    }

    /// <summary>
    /// 获取加密密钥（优先级：注入的密钥 > 环境变量 > 配置文件 > 默认值）
    /// </summary>
    private static byte[] GetEncryptionKey()
    {
        if (_cachedKey != null)
            return _cachedKey;

        lock (_keyLock)
        {
            if (_cachedKey != null)
                return _cachedKey;

            // 生成固定的应用程序盐值
            var appSalt = Encoding.UTF8.GetBytes("SumerCoreDevOps-App-Salt-2024");
            
            // 0. 最高优先级：EncryptionKeyManager 注入的密钥
            if (!string.IsNullOrEmpty(_injectedKey))
            {
                _cachedKey = DeriveKeyFromPassword(_injectedKey, appSalt);
                return _cachedKey;
            }

            // 1. 优先从环境变量读取
            var envKey = Environment.GetEnvironmentVariable("SUMER_ENCRYPTION_KEY");
            if (!string.IsNullOrEmpty(envKey))
            {
                _cachedKey = DeriveKeyFromPassword(envKey, appSalt);
                return _cachedKey;
            }

            // 2. 尝试从配置文件读取（appsettings.json）
            var configKey = TryReadKeyFromConfig();
            if (!string.IsNullOrEmpty(configKey))
            {
                _cachedKey = DeriveKeyFromPassword(configKey, appSalt);
                return _cachedKey;
            }

            // 3. 使用默认密钥（仅用于开发环境，生产环境必须配置！）
            Console.WriteLine("⚠️  警告: 未配置加密密钥，使用默认密钥（不安全）！");
            Console.WriteLine("    请设置环境变量 SUMER_ENCRYPTION_KEY 或在 appsettings.json 中配置 Security:EncryptionKey");

            _cachedKey = SHA256.HashData(
                Encoding.UTF8.GetBytes("SumerCoreDevOps-Default-Encryption-Key-Change-In-Production"));

            return _cachedKey;
        }
    }

    /// <summary>
    /// 尝试从配置文件读取密钥
    /// </summary>
    private static string? TryReadKeyFromConfig()
    {
        try
        {
            var configPath = Path.Combine(AppContext.BaseDirectory, "appsettings.json");
            if (!File.Exists(configPath))
                return null;

            var json = File.ReadAllText(configPath);

            // 简单的 JSON 解析（避免依赖 Microsoft.Extensions.Configuration）
            var match = System.Text.RegularExpressions.Regex.Match(
                json,
                @"""EncryptionKey""\s*:\s*""([^""]+)""",
                System.Text.RegularExpressions.RegexOptions.IgnoreCase);

            if (match.Success)
                return match.Groups[1].Value;

            return null;
        }
        catch
        {
            return null;
        }
    }

    /// <summary>
    /// 加密字符串（AES-256-CBC）
    /// </summary>
    public static string Encrypt(string plainText, byte[]? customKey = null)
    {
        if (string.IsNullOrEmpty(plainText))
            return plainText;

        // 获取加密密钥
        var masterKey = customKey ?? GetEncryptionKey();
        
        // 生成随机盐值和IV
        var salt = GenerateSalt();
        using var aes = Aes.Create();
        aes.GenerateIV();
        
        // 使用主密钥和随机盐值派生实际加密密钥
        var encryptionKey = DeriveKeyFromPassword(Encoding.UTF8.GetString(masterKey), salt);
        aes.Key = encryptionKey;
        aes.Mode = CipherMode.CBC;
        aes.Padding = PaddingMode.PKCS7;

        using var encryptor = aes.CreateEncryptor();
        var plainBytes = Encoding.UTF8.GetBytes(plainText);
        var encryptedBytes = encryptor.TransformFinalBlock(plainBytes, 0, plainBytes.Length);

        // 盐值 + IV + 加密数据
        var result = new byte[salt.Length + aes.IV.Length + encryptedBytes.Length];
        Buffer.BlockCopy(salt, 0, result, 0, salt.Length);
        Buffer.BlockCopy(aes.IV, 0, result, salt.Length, aes.IV.Length);
        Buffer.BlockCopy(encryptedBytes, 0, result, salt.Length + aes.IV.Length, encryptedBytes.Length);

        return Convert.ToBase64String(result);
    }

    /// <summary>
    /// 解密字符串（AES-256-CBC）
    /// </summary>
    public static string Decrypt(string encryptedText, byte[]? customKey = null)
    {
        if (string.IsNullOrEmpty(encryptedText))
            return encryptedText;

        try
        {
            var masterKey = customKey ?? GetEncryptionKey();
            var encryptedBytes = Convert.FromBase64String(encryptedText);

            // 验证数据长度是否足够
            const int saltLength = 32; // 256位盐值
            const int ivLength = 16;   // 128位IV
            if (encryptedBytes.Length < saltLength + ivLength)
            {
                // 可能是旧版本格式的数据，尝试兼容处理
                return DecryptLegacyFormat(encryptedBytes, masterKey);
            }

            // 提取盐值和IV
            var salt = new byte[saltLength];
            var iv = new byte[ivLength];
            Buffer.BlockCopy(encryptedBytes, 0, salt, 0, saltLength);
            Buffer.BlockCopy(encryptedBytes, saltLength, iv, 0, ivLength);

            // 使用主密钥和盐值派生实际解密密钥
            var decryptionKey = DeriveKeyFromPassword(Encoding.UTF8.GetString(masterKey), salt);

            using var aes = Aes.Create();
            aes.Key = decryptionKey;
            aes.IV = iv;
            aes.Mode = CipherMode.CBC;
            aes.Padding = PaddingMode.PKCS7;

            using var decryptor = aes.CreateDecryptor();
            var decryptedBytes = decryptor.TransformFinalBlock(
                encryptedBytes, saltLength + ivLength, encryptedBytes.Length - saltLength - ivLength);

            return Encoding.UTF8.GetString(decryptedBytes);
        }
        catch (FormatException)
        {
            // 如果不是 Base64 格式，说明可能是明文存储，直接返回
            return encryptedText;
        }
        catch (CryptographicException)
        {
            // 解密失败，可能密钥不匹配或数据损坏
            throw new InvalidOperationException("密码解密失败，可能密钥不正确或数据已损坏");
        }
        catch (Exception ex)
        {
            throw new InvalidOperationException($"解密过程发生错误: {ex.Message}", ex);
        }
    }

    /// <summary>
    /// 解密旧格式的加密数据（兼容处理）
    /// </summary>
    private static string DecryptLegacyFormat(byte[] encryptedBytes, byte[] key)
    {
        try
        {
            using var aes = Aes.Create();
            aes.Key = key;
            aes.Mode = CipherMode.CBC;
            aes.Padding = PaddingMode.PKCS7;

            // 提取 IV（前 16 字节）
            var iv = new byte[16];
            Buffer.BlockCopy(encryptedBytes, 0, iv, 0, 16);
            aes.IV = iv;

            using var decryptor = aes.CreateDecryptor();
            var decryptedBytes = decryptor.TransformFinalBlock(
                encryptedBytes, 16, encryptedBytes.Length - 16);

            return Encoding.UTF8.GetString(decryptedBytes);
        }
        catch
        {
            // 如果解密失败，抛出原始异常
            throw new InvalidOperationException("密码解密失败，可能密钥不正确或数据已损坏");
        }
    }

    /// <summary>
    /// 生成安全的随机密钥
    /// </summary>
    public static byte[] GenerateKey()
    {
        using var rng = RandomNumberGenerator.Create();
        var key = new byte[32]; // 256 位
        rng.GetBytes(key);
        return key;
    }

    /// <summary>
    /// 生成安全的随机盐值
    /// </summary>
    public static byte[] GenerateSalt()
    {
        using var rng = RandomNumberGenerator.Create();
        var salt = new byte[32]; // 256 位盐值
        rng.GetBytes(salt);
        return salt;
    }

    /// <summary>
    /// 从密码生成密钥（使用随机盐值，增强安全性）
    /// </summary>
    public static byte[] DeriveKeyFromPassword(string password, byte[] salt)
    {
        if (salt == null || salt.Length < 8)
        {
            throw new ArgumentException("盐值必须至少8字节长", nameof(salt));
        }
        
        // 增加迭代次数以提高安全性
        using var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 200000, HashAlgorithmName.SHA256);
        return pbkdf2.GetBytes(32); // 256 位密钥
    }
}
